import requests
import json
import re
from lxml import etree
import ddddocr
import os
import pickle
import time
from csm.utils import del_prolist_attrs, extract_search_details, extract_product_details

module_src = os.path.abspath(__file__)
module_dir = os.path.dirname(module_src)

BASE_URL = "https://supplier.casmart.com.cn/product/basic"


class SessionManager:
    """
    会话维持类

    Args:
        session_file (string): 会话文件名
    """

    USERNAME = "18826469064"  # 用户名
    PASSWORD = "nbzqyy18826469064"  # 密码
    LOGIN_URL = "https://sso.casmart.com.cn/login?channel-id=2&return-url=https://www.casmart.com.cn/"  # 登录地址
    CHANNEL_ID = "2"  # 渠道ID
    RETURN_URL = "https://www.casmart.com.cn/"

    def __init__(self, session_file: str = "buyyer_session.pkl"):
        self.session_file = session_file
        self.session = requests.Session()  # 创建共用会话

        # 添加请求头
        self.session.headers.update(
            {
                "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
                "Referer": "https://www.casmart.com.cn/",
                "Origin": "https://www.casmart.com.cn",
            }
        )
        self.load_session()

    def load_session(self):
        """
        加载持久化cookies
        """
        if os.path.exists(
            f"{module_dir}/{self.session_file}"
        ):  # 判断是否存在 session 文件
            try:
                with open(
                    f"{module_dir}/{self.session_file}", "rb"
                ) as f:  # 存在就加载 session 文件
                    session = pickle.load(f)

                    self.session.cookies.update(session.cookies)
                    if self.check_login():
                        print("Session 有效")
                    else:
                        self.init_login()
            except Exception as e:
                print("加载 cookies 失败：", e)

        else:  # 不存在就直接重新初始化 Session 文件
            self.init_login()

    def save_session(self):
        """持久化当前 session （包括 cookies）"""
        with open(f"{module_dir}/{self.session_file}", "wb") as f:
            pickle.dump(self.session, f)
        print(f"会话 cookies 已保存到 {self.session_file}")

    def check_login(self):
        """
        检查当前 session是否已登录
        通过访问用户资料的方式检测登录状态
        """

        DEATAIL_URL = "https://supplier.casmart.com.cn/back-apply/detail"
        resp = self.session.get(
            url=DEATAIL_URL,
            # proxies={"https": "http://127.0.0.1:9090"},
            # verify=False,
        )
        if resp.status_code == 200:
            return True
        return False

    def _get_verify_code(self, max_retries: int = 3) -> str:
        """获取验证码

        Args:
            max_retries (int, optional): 默认重试次数. Defaults to 3.

        Raises:
            Exception: 获取验证码图片异常

        Returns:
            str: 验证码字符串
        """
        VERYFY_URL = "https://sso.casmart.com.cn/verify-code"
        # res_verify = self.session.get(url=VERYFY_URL)  # 请求验证码
        res_verify = self.session.get(
            url=VERYFY_URL,
            # proxies={"https": "http://127.0.0.1:9090"},
            # verify=False,
        )  # 请求验证码

        if res_verify.status_code == 200:
            ocr = ddddocr.DdddOcr(show_ad=False)
            verify_code = ocr.classification(res_verify.content)
            # print(f"VerifyCode: {verify_code}")
            return verify_code
        else:
            raise Exception(f"❌获取验证码失败")

    def _get_client_key(self, verify_code: str):  # 获取登录凭证
        """发起登录，获取 client_key

        Args:
            verify_code (str): 验证码

        Raises:
            Exception: _description_

        Returns:
            _type_: _description_
        """

        LOGIN_URL = "https://sso.casmart.com.cn/login"
        heardes = {  # 请求头
            "Origin": "https://sso.casmart.com.cn/",
            "content-type": "application/json",
        }

        user_data = {  # 登录参数
            "channelId": self.CHANNEL_ID,
            "returnUrl": "https://www.casmart.com.cn",
            "userName": self.USERNAME,
            "password": self.PASSWORD,
            "verifycode": verify_code,
        }

        response = self.session.post(
            url=LOGIN_URL,
            headers=heardes,
            data=json.dumps(user_data),
            # proxies={"https": "http://127.0.0.1:9090"},
            # verify=False,
        )

        if response.status_code == 200:
            print(response.json())
            print(response.cookies)
            return response.json()["data"]
        else:
            print(response.json()["data"])
            raise Exception("❌登录失败")

    def _post_redirect(self, client_key):
        """
        换取登录session

        携带`client_key`请求后,会自动跳转到`returnUrl`地址并设置`cookie`
        """
        REDIRECT_URL = "https://sso.casmart.com.cn/redirect"

        data = {
            "returnUrl": self.RETURN_URL,
            "channelId": self.CHANNEL_ID,
            "clientKey": client_key,
        }

        # response = self.session.post(url=REDIRECT_URL, data=data)
        response = self.session.post(
            url=REDIRECT_URL,
            data=data,
            # proxies={"https": "http://127.0.0.1:9090"},
            # verify=False,
        )

        if response.status_code == 200:
            print("✅登录成功")
        else:
            raise Exception("❌获取登录 Session 失败")

    def _set_cookies(self):
        """设置 cookies"""
        url = "https://www.casmart.com.cn/setCookies"
        resp = self.session.post(
            url=url,
            # proxies={"https": "http://127.0.0.1:9090"},
            # verify=False,
        )

        if resp.status_code == 200:
            self.save_session()
            print(resp.cookies)
            print("✅设置 cookies 成功")
        else:
            raise Exception("❌设置 cookies 失败")

    def init_login(self):
        """
        初始化登录
        """
        max_retries = 5
        for attempt in range(1, max_retries + 1):
            try:
                # 1.获取验证码
                verify_code = self._get_verify_code()
                # 2.获取登录凭证
                key = self._get_client_key(verify_code)
                # 3.获取登录 cookie
                self._post_redirect(key)
                # 4.设置 cookies
                self._set_cookies()
                # 登录成功，终止循环
                break

            except Exception as e:
                print(f"第{attempt}次尝试失败：{e}")
                if attempt == max_retries:
                    print("已达到最大重试次数，操作失败。")
                else:
                    time.sleep(1)  # 等待1秒后重试


class RequestHanlder(SessionManager):  # 请求类 继承于`SessionManager`
    """
    请求类 继承于`SessionManager`
    """

    def __init__(self, session_file="buyyer_session.pkl"):
        super().__init__(session_file)
        self._sem = 3  # 并发数量
        # headers = {
        #     "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36",
        #     "Referer": "https://supplier.casmart.com.cn/",
        # }
        # self.async_session = aiohttp.ClientSession(
        #     headers=headers, cookies=self.cookies
        # )

    def get_product_list(self, data: dict = {}) -> dict:
        """
        获取商品列表
        tabIndex 搜索类型 0全部 1上架 2下架 3系统下架 4未审核 5待补充 6审核不通过
        """
        SEARCH_URL = f"{BASE_URL}/list"

        json_data = {
            "page": 1,
            "count": 50,
            "params": {
                "brandName": "",
                "cateId": "",
                "tabIndex": "0",
                "stationId": "",
                "brandId": "",
                "deliveryId": "",
                "searchType": "0",
                "search": "",
            },
        }
        json_data.update(data)

        resp = self.session.post(url=SEARCH_URL, json=json_data)
        return resp.json()

    def del_product(self, ids: list) -> dict:
        """
        删除指定商品

        Args:
            data (_type_): _description_
        """
        url = f"{BASE_URL}/updateStatus"
        json_data = {"ids": ",".join(str(item) for item in ids), "trash": 1, "type": 2}
        resp = self.session.post(url=url, json=json_data)
        return resp.json()

    def get_saller_prolist(
        self, domain: str, page_num: int, order_by: str = "hotsale_down"
    ) -> list:
        """获取其他商家商品销售列表

        Args:
            domain (str): 商家店铺 domain
            page_num (int): 页码
            order_by (str): 销量排序 默认降序
        Raises:
            Exception: _description_

        Returns:
            list: _description_
        """
        SEARCH_URL = f"{domain}/shop/products"
        headers = {
            "Accept": "application/json, text/javascript, */*; q=0.01",
            "Accept-Language": "zh-CN,zh;q=0.9",
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "Content-Type": "application/json",
            "Origin": domain,
            "Pragma": "no-cache",
            "Referer": SEARCH_URL,
        }
        self.session.headers.update(headers)

        max_retries = 5
        retry_delay = 1

        # region 搜索测试
        data = {
            "initPage": True,
            "pageNum": page_num,
            "pageSize": 20,
            "orderBy": order_by,
            "supplierCateId": "-1",
            "queryString": "",
            "minPrice": None,
            "maxPrice": None,
            "queryType": "pro",
        }
        data = json.dumps(data, separators=(",", ":"))
        for attempt in range(max_retries):
            resp = self.session.post(url=SEARCH_URL, data=data)
            if resp.status_code == 200:
                return del_prolist_attrs(resp.json()["dataList"])
            else:
                print(f"获取数据失败，{attempt+1}/{max_retries}次尝试")
                if attempt < max_retries - 1:
                    time.sleep(retry_delay)
        raise Exception(f"{page_num}页获取失败❌")

    def get_saller_prodes(self, supplier_id: int, pro_id: int):

        deatil_url = (
            f"https://www.casmart.com.cn/product-details/page/{supplier_id}/{pro_id}"
        )
        resp = self.session.get(url=deatil_url)
        if resp.status_code == 200:
            return resp.text
        else:
            raise Exception(f"获取商品详情失败❌")

    def search_pro(self, search_data: dict = {}) -> list[dict]:
        """
        商城搜索商品
        使用时需要先通过简单搜索获取 cookie
        Args:
            search_data (dict, optional): 搜索参数. Defaults to {}.

        Returns:
            list[dict]: 搜索结果列表
        """
        SEARCH_URL = "https://www.casmart.com.cn/n/search"

        headers = {
            "Referer": "https://www.casmart.com.cn/n/search",
        }

        # query_data = {"q": "安耐吉"}
        query_data = {"q": "安耐吉"}
        query_data.update(search_data)
        resp = self.session.get(
            url=SEARCH_URL,
            params=query_data,
            headers=headers,
            # proxies={"https": "http://127.0.0.1:9090"},
            # verify=False,
        )
        if resp.status_code == 200:
            return extract_search_details(resp.text)
        else:
            raise Exception(f"搜索商品失败❌")

    def get_pro_detail(self, product_detail_url: str) -> dict:
        """获取商品详情"""
        resp = self.session.get(
            url=product_detail_url,
            # proxies={"https": "http://127.0.0.1:9090"},
            # verify=False,
        )
        if resp.status_code == 200:
            return extract_product_details(resp.text)
        else:
            raise Exception(f"获取商品详情失败❌")

# async def main():
#     """
#     异步任务启动器
#     """
#     roler = "Buyer"  # Buyer || Seller
#     ex1 = RequestHanlder(roler)

#     # tasks = [ex1.async_product_fetch(page_num) for page_num in range(1, 500)]
#     tasks = [ex1.async_fetch(page) for page in range(1401, 1501)]
#     results = await asyncio.gather(*tasks)
#     await ex1.async_close()
#     with open("./1500data.json", "w", encoding="utf-8") as f:
#         f.write(json.dumps(results, ensure_ascii=False))
#     print("任务完成")
